#include <iostream>
#include <thread>
#include <mutex>
#include <condition_variable>
#include <atomic>
#include <vector>
#include <functional>
#include <future>
#include <memory>

using functor = std::function<void(void)>;
class ThreadPool
{
public:
    ThreadPool(int threadNum = 1)
        : _stop(false)
    {
        for (int i = 0; i < threadNum; i++)
        {
            _threads.emplace_back(&ThreadPool::entry, this);
        }
    }

    // 在进行push任务的时候，我们并不知道用户传入的函数的参数是什么和返回值
    // 我们要使用不定参来传递，push函数内部会将传入进来的函数封装成为一个异步任务（packaged_task）
    // 使用lambda生成一个可调用对象（内部异步执行），抛入到任务池中，交给线程来执行
    template <class F, class... Args>
    auto Push(F &&func, Args &&...args) -> std::future<decltype(func(args...))>
    // decltype依靠函数名和参数来推导函数的返回值
    {
        // 1.将传入的函数封装成一个packaged_task任务
        auto tmp_func = std::bind(&std::forward<F>(func), std::forward<Args>(args)...);
        using return_type = decltype(func(args...));
        auto task = std::make_shared<std::packaged_task<return_type()>>(tmp_func);
        std::future<return_type> fu = task->get_future();

        // 2.构造一个lambda匿名函数（捕捉任务对象），函数内执行任务对象
        {
            std::unique_lock<std::mutex> lock(_mutex);
            _taskpool.push_back([task]()
                                { (*task)(); });
            // 3.将该匿名函数对象抛入任务池中去
            _cv.notify_one(); // 唤醒线程执行任务
        }
        return fu;
    }
    void stop()
    {
        _stop = true;
        _cv.notify_all();             // 将所有的线程唤醒
        for (auto &thread : _threads) // 将所有的线程回收了
        {
            thread.join();
        }
    }
    ~ThreadPool()
    {
        stop();
    }

private:
    // 线程入口函数 -- 不断的从任务池中取出任务执行
    void entry()
    {
        while (!_stop)
        {
            std::vector<functor> tmp_taskpool;
            {
                // 加锁
                std::unique_lock<std::mutex> lock(_mutex);
                // 等待任务池不为空，或者_stop被置位时返回
                _cv.wait(lock, [this]()
                         { return _stop || !_taskpool.empty(); });
                // 第二个参数返回值为false，表示阻塞该线程，释放lock
                tmp_taskpool.swap(_taskpool);
            }
            // 执行任务
            for (auto &task : tmp_taskpool)
            {
                task();
            }
        }
    }

private:
    std::atomic<bool> _stop;
    std::mutex _mutex;                 // 锁
    std::condition_variable _cv;       // 信号量
    std::vector<std::thread> _threads; // 线程池
    std::vector<functor> _taskpool;    // 任务池
};

int Add(int num1, int num2)
{
    return num1 + num2;
}
int main()
{
    ThreadPool pool(5);
    for (int i = 0; i < 10; i++)
    {
        std::future<int> fu = pool.Push(Add, 10, i);
        std::cout << fu.get() << std::endl;
    }
    return 0;
}